package cn.com.youjile.coding;

import cn.com.youjile.entity.MsgInfo;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.List;

/**
 * @Author youjile
 * @CreateDate 2021/3/12
 * @Describe 自定义解码器
 * 低位在前，高位在后
 * [4字节数据长度][2字节数据类型][数据]
 * 为防止粘包，在字符串的二进制码上层添加一层交互协议，每个二进制包前面加六个字节的协议头定义。
 * 其中0-3 表示一个int类型的数据（），4为保留字段 5和6为数据类型。
 *
 * 0 1 2 3 4 5 6
 * 0-8位 9-16位 17-24位 25-32位 33-40（保留字段） 41-48（数据类型） 49-56数据类型
 */
public class GeneralDecoder extends ByteToMessageDecoder {

    private final static Logger logger = LoggerFactory.getLogger(GeneralDecoder.class);

    public GeneralDecoder(){

    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        while (in.readableBytes() > 6) { // 如果可读长度小于包头长度，退出。
            in.markReaderIndex();

            // 获取包头中的body长度
            int s0 = (int) (in.readByte() & 0xff);
            int s1 = (int) (in.readByte() & 0xff);
            int s2 = (int) (in.readByte() & 0xff);
            int s3 = (int) (in.readByte() & 0xff);
            s3 <<= 24;
            s2 <<= 16;
            s1 <<= 8;
            int length = (int) (s0 | s1 | s2 | s3);

            // 获取包头中的数据类型类型
            int s4 = (int) (in.readByte() & 0xff);
            int s5 = (int) (in.readByte() & 0xff);
            s5 <<= 8;
            int dataType = (int) (s4 | s5);
            // 如果可读长度小于body长度，恢复读指针，退出。
            if (in.readableBytes() < length) {
                in.resetReaderIndex();
                return;
            }

            // 读取body
            ByteBuf bodyByteBuf = in.readBytes(length);

            byte[] array = null;
            int offset = 0;

            int readableLen = bodyByteBuf.readableBytes();
            if (bodyByteBuf.hasArray()) {
                array = bodyByteBuf.array();
                offset = bodyByteBuf.arrayOffset() + bodyByteBuf.readerIndex();
            } else {
                array = new byte[readableLen];
                bodyByteBuf.getBytes(bodyByteBuf.readerIndex(), array, 0, readableLen);
                offset = 0;
            }

            //反序列化
            MsgInfo result = decodeBody(dataType, array, offset, readableLen);
            out.add(result);
            ReferenceCountUtil.release(bodyByteBuf);
        }
    }

    public MsgInfo decodeBody(int dataType, byte[] array, int offset, int length) throws Exception {
        if(offset != 0) {
            byte [] b = new byte[length];
            for (int i = offset; i < array.length; i++) {
                b[i-offset] = array[i];
            }
            return new MsgInfo(dataType, b); // or throw exception
        } else {
            return new MsgInfo(dataType, array); // or throw exception
        }
    }

}
